
clSPIDevice::clSPIDevice(PortStruct Port){
	pSPI						= NULL;
	SPISettings.BurstSignal		= true;
	SPISettings.LowBitFirst		= false;
	SPISettings.WaitMISOSignal	= false;
	Connected					= false;
	
	SPISettings.SPIProgSignalSettings.CSDuration	= 0;
}

clSPIHWD::clSPIHWD(PortStruct lSPIPort, ePins lMISO, ePins lMOSI, ePins lSCK, ePins lCS):clSPI(lSPIPort, lMISO, lMOSI, lSCK){
	PinUp(*lSPIPort.DDRX, lCS);
	CS	= lCS;
}

clSPISettings::clSPISettings(void){}

void clSPI::Connect(){};

void clSPI::DisConnect(){};

void clSPIHWD::Connect(){//clSPIDevice *pDevice
	this->SignalSettings.SPIEnable(true);
//	pDevice->pSPI-> SPISettings.SPISignalSettings.HDWDSignal.SPIEnable(true);
}

void clSPIHWD::DisConnect(){
	this->SignalSettings.SPIEnable(false);
//	pDevice->SPISettings.SPISignalSettings.HDWDSignal.SPIEnable(false);
}

void clSPIProg::Connect(){}

void clSPIProg::DisConnect(){}

void clSPIDevice::Connect(){
	if (pSPI == NULL){
		Connected	= false;
		return;
	};
	
	pSPI->Connect();
	Connected	=	true;
}

void clSPIDevice::DisConnect(){
	pSPI->DisConnect();
	Connected	=	false;
}

clSPI::clSPI(PortStruct lPort, ePins lMISO, ePins lMOSI, ePins lSCK) {
	Port	= lPort;
	MISO	= lMISO;
	MOSI	= lMOSI;
	SCK		= lSCK;
	
	PinsUp(*lPort.DDRX, _BV(lMOSI) + _BV(lSCK));
//	PinUp(Port.DDRX, lMOSI);
//	PinUp(Port.DDRX, lSCK);
	PinDown(*Port.DDRX, lMISO);
}

void clReg8WSPI::SetValue(unsigned char lVal){
	Value	= lVal;
	SendToSPI();
}

//void clSPIDeviceWReg::RegsInit(void){};

//clSPIDeviceWReg::clSPIDeviceWReg(PortStruct *Port): clSPIDevice(Port){
	//	RegsInit();
//}

clSPIDeviceWReg::clSPIDeviceWReg(clSPI *lpISP, PortStruct lPort, ePins lCSPin): clSPIDevice(lPort){
	SPISettings.CSPin	= lCSPin;
	pSPI				= lpISP;
	
	PinUp(*lPort.DDRX, lCSPin);
	PinUp(*lPort.PortX, lCSPin);
}

void clSPIDevice::SetSPI(clSPI * lSPI){
	pSPI	= lSPI;
}

bool clSPIDevice::GetConnectStatus(void){
	return Connected;
}

void clSPIDevice::SendData(uint8_t *data, uint8_t length){
	pSPI->SendData(this, data, length);
}

void clReg8WSPI::SendToSPI(void){
    unsigned char Data[2] = {(unsigned char)Address, (unsigned char)Value};
	
	if (pDevice->pSPI != NULL){
		pDevice->pSPI->SendData(pDevice, Data, (unsigned char) 2);
	};
}

void clSPIHWD::SendData(clSPIDevice *lpDevice, unsigned char *Bytes, unsigned char ByteCount){
	if (!SignalSettings.SPCRREG.VAL.vSPIE & !SignalSettings.SPCRREG.VAL.vSPE) {
		SignalSettings.CPUMaster(true);
		SignalSettings.SPIEnable(true);
	};

	SPCR	= SignalSettings.SPCRREG.BYTE;
	SPSR	= SignalSettings.SPSRREG.BYTE;
	_SendData(lpDevice, Bytes, ByteCount);
}


void clSPI::SendData(clSPIDevice *lpDevice, unsigned char *Bytes, unsigned char ByteCount){
	_SendData(lpDevice, Bytes, ByteCount);
}


void clSPI::_SendData(clSPIDevice *lpDevice, unsigned char *Bytes, unsigned char ByteCount){
	SelectDevice(lpDevice->SPISettings.CSPin);
	
	if (lpDevice->SPISettings.WaitMISOSignal) {WaitMISO;}

	unsigned char lByteIndex = 0;
	
	while (lByteIndex < ByteCount){
		SendByte(lpDevice, &Bytes[lByteIndex]);
		
		if (++lByteIndex < ByteCount &&
				!lpDevice->SPISettings.BurstSignal) {
			UnSelectDevice(lpDevice->SPISettings.CSPin);
			SelectDevice(lpDevice->SPISettings.CSPin);
			if (lpDevice->SPISettings.WaitMISOSignal) {WaitMISO;};
		}
	}

	UnSelectDevice(lpDevice->SPISettings.CSPin);
}

inline void clSPI::SelectDevice(const ePins CSPin){
	PinDown(*Port.PortX, CSPin);
	clTimer0::PauseUSx8(12);
}

inline void clSPI::UnSelectDevice(const ePins CSPin){
	PinUp(*Port.PortX, CSPin);
	clTimer0::PauseUSx8(12);
}

void clSPI::SendByte(clSPIDevice *lpDevice, unsigned char *Byte){}

void clSPIHWD::SendByte(clSPIDevice *lpDevice, unsigned char *Byte){
	SPDR	= *Byte;
	while (!(SPSR & (1<<SPIF)));
	*Byte	= SPDR;
}

void clSPIProg::SCKHoldUp(const clSPIDevice *lpDevice){
	const unsigned long *lpCSSignalDuration	= &lpDevice->SPISettings.SPIProgSignalSettings.CSDuration;

	PinUp(*Port.PortX, SCK);
	clTimer0::PauseUSx8(uint8_t(*lpCSSignalDuration/8));
//	PauseUS(*lpCSSignalDuration);
}

void clSPIProg::SCKHoldDown(const clSPIDevice *lpDevice){
	const unsigned long *lpCSSignalDuration	= &lpDevice->SPISettings.SPIProgSignalSettings.CSDuration;

	PinDown(*Port.PortX, SCK);
	clTimer0::PauseUSx8(uint8_t(*lpCSSignalDuration/8));
//	PauseUS(*lpCSSignalDuration);
}

void clSPIProg::SCKToDown(){
	PinDown(*Port.PortX, SCK);
}

void clSPIProg::SendByte(clSPIDevice *lpDevice, unsigned char *Byte){

	SCKToDown();

	unsigned char BitIndex, lByte;
	
	lByte	= *Byte;
	*Byte	= 0;
	unsigned char lMaskOut	= lpDevice->SPISettings.LowBitFirst ? 0x01:0x80;
	unsigned char lMaskIn	= lpDevice->SPISettings.LowBitFirst ? 0x80:0x01;
	
	for (BitIndex = 0; BitIndex < 8; BitIndex++){
		*Byte	= lpDevice->SPISettings.LowBitFirst ? *Byte >> 1: *Byte << 1;
		
		if (lByte & lMaskOut){
			PinUp(*Port.PortX, MOSI);
		}
		else{
			PinDown(*Port.PortX, MOSI);
		};
		SCKHoldUp(lpDevice);
		*Byte	|= (*Port.PinX & (1 << MISO))? lMaskIn: 0;
		SCKHoldDown(lpDevice);

		lByte	= lpDevice->SPISettings.LowBitFirst ? lByte >> 1: lByte << 1;
	}
}

void clSPIHDWDSignalSettings::SPIInterupt(const bool Val){
	SPCRREG.VAL.vSPIE	= Val;
	SPCR				= SPCRREG.BYTE;
}

void clSPIHDWDSignalSettings::SPIEnable(const bool Val){
	SPCRREG.VAL.vSPE	= Val;
	SPCR				= SPCRREG.BYTE;
}

void clSPIHDWDSignalSettings::DataOrder(const bool Val){
	SPCRREG.VAL.vDORD	= Val;
	SPCR				= SPCRREG.BYTE;
}

void clSPIHDWDSignalSettings::CPUMaster(const bool Val){
	SPCRREG.VAL.vMSTR	= Val;
	SPCR				= SPCRREG.BYTE;
}

void clSPIHDWDSignalSettings::ClockPolarity(const bool Val){
	SPCRREG.VAL.vCPOL	= Val;
	SPCR				= SPCRREG.BYTE;
}

void clSPIHDWDSignalSettings::ClockPhase(const bool Val){
	SPCRREG.VAL.vCPHA	= Val;
	SPCR				= SPCRREG.BYTE;
}

void clSPIHDWDSignalSettings::ClockRate(const SPI_RATE Rate){
	SPCRREG.VAL.vSPR	= Rate;
	SPCR				= SPCRREG.BYTE;
}

void clSPIHDWDSignalSettings::InteruptFlag(const bool Val){
	SPSRREG.VAL.vSPIF	= Val;
	SPSR				= SPSRREG.BYTE;
}

void clSPIHDWDSignalSettings::CollisionFlag(const bool Val){
	SPSRREG.VAL.vWCOL	= Val;
	SPSR				= SPSRREG.BYTE;
}

void clSPIHDWDSignalSettings::DoubleSpeed(const bool Val){
	SPSRREG.VAL.vSPI2X	= Val;
	SPSR				= SPSRREG.BYTE;
}

clSPIProg::clSPIProg(PortStruct lPort, ePins lMISO, ePins lMOSI, ePins lSCK):clSPI(lPort, lMISO, lMOSI, lSCK){}

void clSPIDeviceWReg::RegsSend(void){}

void clSPIDeviceWReg::Connect(){
	clSPIDevice::Connect();
	
	if (Connected)
	{
		RegsSend();
	};
}

void clSPIDeviceWReg::DisConnect(){
	clSPIDevice::DisConnect();
}

bool clSPIDevice::GetState(void){
	return Connected;
}
